<?php
/**
 * Notes:
 * User: wudg <544038230@qq.com>
 * Date: 2025/9/18 10:31
 */

namespace App\Service\Amount;


use App\Constants\System\SysStatusConst;
use App\Constants\User\UserAmountTypeConst;
use App\Dao\Users\UserCategoryDao;
use App\Dao\Users\UsersAccountDao;
use App\Dao\Users\UsersAmountLogsDao;
use App\Dao\Users\UsersDao;
use App\Exception\ServiceException;
use App\Model\Users\UsersAmountLog;
use App\Service\User\UserService;
use App\Utils\AmountUtils;
use App\Utils\RequestHeaderUtil;
use Hyperf\Database\Model\Model;
use Hyperf\DbConnection\Db;
use App\Dao\Users\UsersCardsInfoDao;
use Hyperf\Di\Annotation\Inject;
use Exception;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use function Hyperf\Config\config;

class AmountLogService
{

    #[Inject]
    protected UsersCardsInfoDao $usersCardsInfoDao;


    #[Inject]
    protected UsersAmountLogsDao $usersAmountLogsDao;



    #[Inject]
    protected UsersAccountDao $usersAccountDao;

    #[Inject]
    protected UsersDao $usersDao;


    #[Inject]
    protected UserCategoryDao $userCategoryDao;

    /**
     * 会员卡二维码核销
     * @param string $cardNo
     * @param float $amount
     * @param int $cateId
     * @param int $accountId
     * @return array
     */
    public function userCardCancellation(array $params)
    {
        $userInfo = auth()->user();
        $disAmount = AmountUtils::yuanTofen($params['amount']);
        list($cardNo,$time) = explode("#",$params['card_no']);
        if(empty($time) || $time + config('card.time_out') < time())
        {
            throw new ServiceException("二维码已过期");
        }
        $userCardInfo = $this->usersCardsInfoDao->getCardInfoByCardNo($cardNo);
        if(!$userCardInfo) throw new ServiceException("会员卡不存在");
        $cardUserAccountInfo = $this->usersAccountDao->getSysAccountInfo($userCardInfo->uid);
        if(!$cardUserAccountInfo) throw new ServiceException("客户账户不存在");
        $accountInfo = $this->usersAccountDao->getInfoByPk($params['account_id']);
        if(!$accountInfo) throw new ServiceException("动账账户不存在");
        $userCardCids = json_decode($userCardInfo->card_cids,true);

        $cateInfo = $this->userCategoryDao->getCateById($params['cate_id']);
        if(!$cateInfo) throw new ServiceException("类目不存在");
        if($userCardInfo->amount < $disAmount)
        {
            throw new ServiceException("余额不足，请先充值");
        }
        if(!in_array($cateInfo->id,$userCardCids)) throw new ServiceException("当前会员卡不支持此项目类目消费");
        $ip = RequestHeaderUtil::getClientIp();
        Db::beginTransaction();
        try {
            //扣除会员卡对应面值金额
            $userCardInfo->amount = bcsub($userCardInfo->amount,$disAmount);
            $userCardInfo->save();

            /**
             * 客户账户动账
             */
            $cardUserAccountInfo->out_amount = bcadd($userCardInfo->amount,$disAmount);
            $cardUserAccountInfo->amount = bcsub($cardUserAccountInfo->amount,$disAmount);
            $cardUserAccountInfo->save();

            //会员卡消费不增加店铺账号的资金账户收入情况 办卡时已收取过一次 防止重复记录导致账目错误
            $relation_info = [
                'company_name' => $userInfo->company_name,
            ];
            /**
             * 客户账户动账流水
             */
            $this->usersAmountLogsDao->add(new UsersAmountLog(),[
                'uid' => $cardUserAccountInfo->uid,
                'soft_id' => $cardUserAccountInfo->uid,
                'account_id' =>$cardUserAccountInfo->id,
                'card_no' =>$userCardInfo->card_no,
                'type' =>UserAmountTypeConst::EXPENSE->value,
                'ip' => $ip,
                'amount' =>$disAmount,
                'cate_id' => $cateInfo->id,
                'save_date' => $params['save_date'],
                'remark' => "会员卡消费",
                'relation_info' => json_encode($relation_info,JSON_UNESCAPED_UNICODE)
            ]);
            $relation_company_info = [
                'custom_name' =>$cardUserAccountInfo->name ?? "",
                'phone' =>$cardUserAccountInfo->phone ?? "",
                'user_name' =>$userCardInfo->user_name,
                'card_id' =>$userCardInfo->id,
            ];
            //增加店铺账号流水入账
            $amountLogInfo = $this->saveAmountLogs($userInfo,[
                'uid' => $userInfo->getId(),
                'account_id' =>$params['account_id'],
                'card_no' =>$userCardInfo->card_no,
                'amount' =>$disAmount,
                'save_date' => $params['save_date'],
                'type' => UserAmountTypeConst::INCOME->value,
                'cate_id' => $params['cate_id'],
                'relation_info' => json_encode($relation_company_info,JSON_UNESCAPED_UNICODE),
                'remark' => $params['remark'] ?? '会员卡核销',
            ]);
            Db::commit();
            $amountLogs = $amountLogInfo->toArray();
            $amountLogs['amount'] = AmountUtils::fenToYuan($amountLogs['amount']);
            return $amountLogs;
        }catch (Exception $e)
        {
            Db::rollBack();
            throw new ServiceException($e->getMessage());
        }
    }

    /**
     * 流水记录修改
     * @param array $params
     * @return array
     */
    public function editAmountLog(array $params)
    {
        $userInfo = auth()->user();
        $amountLogInfo = $this->usersAmountLogsDao->getInfoByPk($params['id']);
        if(!$amountLogInfo || $amountLogInfo->uid != $userInfo->getId())
        {
            throw new ServiceException("记录不存在");
        }
        $accountInfo = $this->usersAccountDao->getInfoByPk($params['account_id']);
        if(!$accountInfo || $accountInfo->uid != $userInfo->soft_id) throw new ServiceException("账户不存在");
        $cateInfo = $this->userCategoryDao->getCateById($params['cate_id']);
        if(!$cateInfo  || $cateInfo->uid != $userInfo->soft_id) throw new ServiceException("类目不存在");
        $amountLogInfo->cate_id = $cateInfo->id;
        $amountLogInfo->account_id  = $accountInfo->id;
        $amountLogInfo->amount = AmountUtils::yuanTofen($params['amount']);
        if(!empty($params['remark']))
        {
            $amountLogInfo->remark = trim($params['remark']);
        }
        if(!empty($params['save_date']))
        {
            $amountLogInfo->save_date = date("Y-m-d H:i:s",ceil($params['save_date']/1000));
        }
        $res = $amountLogInfo->save();
        if(!$res) throw new ServiceException("修改失败");
        $amountLogs = $amountLogInfo->toArray();
        $amountLogs['amount'] = AmountUtils::fenToYuan($amountLogs['amount']);
        return $amountLogs;
    }

    public function amountLogsList(array $params)
    {
        $userInfo = auth()->user();
        $amountLogs = UsersAmountLog::with(['cate','account'])
            ->where('uid','=',$userInfo->getId())
            ->where('status','=',SysStatusConst::NORMAL->value);

        if(!empty($params['account_id']))
        {
            $userAccountInfo = $this->getAccountInfo($userInfo,$params['account_id']);
            $amountLogs->where('account_id','=',$userAccountInfo->id);
        }

        if(!empty($params['cate_id']))
        {
            $amountLogs->where('cate_id','=',(int)$params['cate_id']);
        }
        if(!empty($params['start_date']))
        {
            $amountLogs->where('save_date','<',$params['start_date']);
        }
        $amountData = $amountLogs->orderBy('save_date','desc')->orderBy('id','desc')->limit($params['limit'])->get()->toArray();
        foreach($amountData as &$amount)
        {
            $amount['relation_info'] = json_decode($amount['relation_info'],true);
            $amount['amount'] = AmountUtils::fenToYuan($amount['amount']);
            $amount['save_date'] = str_replace("-","/",$amount['save_date']);
            $amount['sort_day'] = strtotime(date("Y-m-d",strtotime($amount['save_date'])));
        }
        return $amountData;
    }


    protected function getAccountInfo($userInfo,$accountId = null)
    {
        if(empty($accountId))
        {
            $userAccountInfo = $this->usersAccountDao->getSysAccountInfo($userInfo->getId());
        }else{
            $userAccountInfo = $this->usersAccountDao->getInfoByPk($accountId);
        }
        if(!$userAccountInfo) throw new ServiceException("客户账户不存在");

        if($userAccountInfo->uid != $userInfo->getId()) throw new ServiceException("账户不存在");

        return $userAccountInfo;
    }

    public function amountLogInfo(int $logId)
    {
        $userInfo = auth()->user();

        $amountLogs = $this->usersAmountLogsDao->getInfoByPk($logId,['cate','account']);

        if(!$amountLogs) throw new ServiceException("记录不存在");

        if($amountLogs->uid != $userInfo->getId()) throw new ServiceException("您的记录不存在");
        $amountLogs = $amountLogs->toArray();
        $amountLogs['relation_info'] = json_decode($amountLogs['relation_info'],true);
        $amountLogs['amount'] = AmountUtils::fenToYuan($amountLogs['amount']);
        $amountLogs['sort_day'] = strtotime(date("Y-m-d", strtotime($amountLogs['save_date'])));
        return $amountLogs;
    }



    public function removeLog(int $logId)
    {
        $userInfo = auth()->user();

        $amountLogs = $this->usersAmountLogsDao->getInfoByPk($logId);
        if(!$amountLogs) throw new ServiceException("记录不存在");

        if($amountLogs->uid != $userInfo->getId()) throw new ServiceException("您的记录不存在");

        $amountLogs->status = SysStatusConst::DELETE->value;

        $res = $amountLogs->save();
        if(!$res) throw new ServiceException("删除失败");
        $amountLogs = $amountLogs->toArray();
        $amountLogs['amount'] = AmountUtils::fenToYuan($amountLogs['amount']);
        return $amountLogs;
    }

    public function monthAmountTotal(string $month,int|null $accountId = null)
    {
        $userInfo = auth()->user();
        $dbRes = $this->usersAmountLogsDao->groupByMonth($userInfo->getId(),$month,$accountId);
        $groupType = $dbRes->toArray();
        $mapGroupAmount  = array_column($groupType,'total_amount','type');
        $inAmount = $mapGroupAmount[UserAmountTypeConst::INCOME->value] ?? 0;
        $outAmount = $mapGroupAmount[UserAmountTypeConst::EXPENSE->value] ?? 0;
        $nowAmount = $inAmount - $outAmount;
        return [
            AmountUtils::fenToyuan($inAmount),
            AmountUtils::fenToyuan($outAmount),
            AmountUtils::fenToyuan($nowAmount),
        ];
    }

    /**
     * 保存动账记录
     * @param array $params
     * @return array
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function amountLogSave(array $params)
    {
        $userInfo = auth()->user();
        $disAmount = AmountUtils::yuanTofen($params['amount']);
        $accountInfo = $this->usersAccountDao->getInfoByPk($params['account_id']);

        if(!$accountInfo) throw new ServiceException("动账账户不存在");
        $cateInfo = $this->userCategoryDao->getCateById($params['cate_id']);
        if(!$cateInfo  || $cateInfo->uid != $userInfo->soft_id) throw new ServiceException("类目不存在");
        Db::beginTransaction();
        try {
            if($params['type'] == UserAmountTypeConst::INCOME->value)
            {
                /**
                 * 客户账户增加
                 */
                $accountInfo->in_amount = bcadd($accountInfo->in_amount,$disAmount);
                $accountInfo->amount = bcadd($accountInfo->amount,$disAmount);
            }else{
                /**
                 * 客户账户减少
                 */
                $accountInfo->out_amount = bcadd($accountInfo->out_amount,$disAmount);
                $accountInfo->amount = bcsub($accountInfo->amount,$disAmount);
            }
            $accountInfo->save();
            //增加店铺账号流水入账
            $amountLogInfo = $this->saveAmountLogs($userInfo,[
                'uid' => $userInfo->getId(),
                'account_id' =>$accountInfo->id,
                'card_no' =>"",
                'relation_info' => '[]',
                'save_date' =>$params['save_date'],
                'type' => $params['type'],
                'amount' =>$disAmount,
                'cate_id' => $cateInfo->id,
                'remark' => $params['remark'] ?? '',
            ]);
            Db::commit();
            $amountLogs = $amountLogInfo->toArray();
            $amountLogs['amount'] = AmountUtils::fenToYuan($amountLogs['amount']);
            $amountLogs['sort_day'] = strtotime(date("Y-m-d", strtotime($amountLogs['save_date'])));
            return $amountLogs;
        }catch (Exception $e)
        {
            Db::rollBack();
            throw new ServiceException($e->getMessage());
        }
    }


    /**
     * 动账处理逻辑
     * @param $userInfo
     * @param array $data
     * @return Model
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function saveAmountLogs($userInfo,array $data)
    {
        $ip = RequestHeaderUtil::getClientIp();
        $data['save_date'] = date("Y-m-d",strtotime($data['save_date']))." ".date('H:i:s');

        /**
         * 子账号处理
         */
        if(UserService::isSubAccount($userInfo))
        {
            $cateInfo = $this->userCategoryDao->getCateById($data['cate_id']);
            if($cateInfo->commission_rate > 0)
            {
                $nameStr = $userInfo->nick_name;

                if(!empty($userInfo->name)) $nameStr = $userInfo->name;
                $amount = bcmul($data['amount'],$cateInfo->commission_rate);
                $softAmount = bcsub($data['amount'],$amount);
                $relation_info = [];
                if(!empty($data['relation_info']))
                {
                    $relation_info = json_decode($data['relation_info'],true);
                }
                $subConfigData = $userInfo->config != null  ? json_decode($userInfo->config,true) : [];
                $subAccountId =  $subConfigData['account_id'] ?? 0;
                $relation_info['commission_rate'] = $cateInfo->commission_rate;
                $subUserInfo = $this->usersDao->getInfoByPk($userInfo->soft_id);
                if($subAccountId == 0) //分享账册为 0 使用默认账册入账
                {
                    $defaultAccount = $this->usersAccountDao->getDefaultAccountInfo($subUserInfo->id);
                    $subAccountId = $defaultAccount->id ?? 0;
                }
                $this->usersAmountLogsDao->add(new UsersAmountLog(),[
                    'uid' => $userInfo->soft_id,
                    'soft_id' => $userInfo->soft_id == 0 ? $userInfo->id : $userInfo->soft_id,
                    'account_id' =>$subAccountId,
                    'card_no' =>$data['card_no'],
                    'type' =>$data['type'],
                    'save_date' =>$data['save_date'],
                    'ip' => $ip,
                    'amount' =>$softAmount,
                    'cate_id' => $data['cate_id'],
                    'remark' => $data['remark']."({$nameStr} 入账按". AmountUtils::yuanTofen(1-$cateInfo->commission_rate)."%计算)",
                    'relation_info' => json_encode($relation_info,JSON_UNESCAPED_UNICODE)
                ]);
                $amountLogInfo = $this->usersAmountLogsDao->add(new UsersAmountLog(),[
                    'uid' => $userInfo->id,
                    'soft_id' => $userInfo->soft_id == 0 ? $userInfo->id : $userInfo->soft_id,
                    'account_id' =>$data['account_id'],
                    'card_no' =>$data['card_no'],
                    'amount' =>$amount,
                    'save_date' =>$data['save_date'],
                    'ip' => $ip,
                    'type' =>$data['type'],
                    'cate_id' => $data['cate_id'],
                    'remark' => $data['remark']." ({$subUserInfo->company_name}设置，入账按". AmountUtils::yuanTofen($cateInfo->commission_rate)."%计算)",
                    'relation_info' => json_encode($relation_info,JSON_UNESCAPED_UNICODE)
                ]);
            }else{//没有设置比例 全部归属子账号
                $data['uid'] = $userInfo->id;
                $data['soft_id'] = $userInfo->soft_id == 0 ? $userInfo->id : $userInfo->soft_id;
                $amountLogInfo = $this->usersAmountLogsDao->add(new UsersAmountLog(),$data);
            }
        }else{
            $data['ip'] = $ip;
            $data['soft_id'] = $userInfo->id;
            $amountLogInfo = $this->usersAmountLogsDao->add(new UsersAmountLog(),$data);
        }
        return $amountLogInfo;
    }
}